﻿package engine.game.world 
{
	import engine.game.player.Player;
	import engine.network.events.SocketRequestEvent;
	import engine.network.events.SocketResponseEvent;
	import flash.events.Event;
	/**
	 * A class that represents the Game world logic in its entirety.
	 * The main game loop will instantiate this class, and call 
	 * accessor methods but will be unaware of the inner workings.
	 * 
	 * @author Jon Smelquist
	 */
	public class GameWorld 
	{
		private final var GAME_ID:int = 1;
		// Vector to hold all the players
		protected var _players:Vector.<Player>;
		// reference to the clients player object to speed up client interactions
		protected var _player:Player;
		// Variable that holds the Map we are currently in
		protected var _map:Map;
		
		/**
		 * Create a new game world.
		 */
		public function GameWorld() 
		{
			// Initialize our Player object container
			_players = new Vector.<Player>();
			// In order to change internal game state we must respond to
			// events dispatched to us.
			initEventHandlers();
		}
		
		/*
		 *	Initialize the game world event handlers in order to respond
		 *	to controllers such as the player or network layers.
		 */
		protected function initEventHandlers()
		{
			stage.addEventListener(SocketResponseEvent.CONNECTED, connectedHandler);
			stage.addEventListener(SocketResponseEvent.LOAD_MAP_CONFIRMATION, loadMapHandler);
			stage.addEventListener(SocketResponseEvent.JOIN_GAME_CONFIRMATION, joinedGameHandler);
			stage.addEventListener(SocketResponseEvent.PLAYER_JOINED, playerJoinedHandler);
			stage.addEventListener(SocketResponseEvent.PLAYER_DISCONNECTED, playerDisconnectedHandler);
			stage.addEventListener(SocketResponseEvent.PLAYER_STATE_CHANGED, playerStateChangedHandler);
		}
		
		/*
		 *	Function that is excecuted upon connection to the server. We
		 * 	can use this event to trigger a Map request from the client
		 * 	and update the internal GameState.
		 * 
		 * 	@param	evt The SocketResponseEvent object associated with this call.
		 */
		protected function connectedHandler(evt:SocketResponseEvent):void
		{
			// We have connected to the server, we can update our gamestate
			// and initiate a Join Game request.
			
			stage.dispatchEvent(new SocketRequestEvent(SocketRequestEvent.LOAD_MAP, GAME_ID,{}));
		}
		
		/*
		 *	Function that is excecuted upon a succesful map request to the 
		 * 	server. We use this event to create the load the relevant Map on 
		 * 	our client, which will then issue a Join Game request.
		 * 	
		 * 	@param	evt The SocketResponseEvent object associated with this call.
		 */
		protected function loadMapHandler(evt:SocketResponseEvent):void
		{
			// We have connected to the server, we can update our gamestate
			// and initiate a Join Game request.
			_map = new Map(evt.parameters.map_name);
			_map.registerObject("CurrentMap");
			// Load the map passing in the callback function to call when load is complete.
			Map.loadMap(_map, mapLoadedHandler);
		}		
		
		/*
		 * 	Function that is excecuted when a Map has finished being loaded.
		 * 	We can now issue our Game request safe in the fact that the Map
		 * 	has been loaded.
		 * 
		 * 	@param	evt The SocketResponseEvent object associated with this call.
		 */
		protected function mapLoadedHandler(e:Event):void
		{
			stage.dispatchEvent(new SocketRequestEvent(SocketRequestEvent.JOIN_GAME, GAME_ID,{}));
		}
		
		/*
		 *	Function that is excecuted once we have joined the game, aka the
		 * 	server has assigned us a playerid which we can use to create a 
		 * 	player locally for our user to control.
		 * 
		 *  @param	evt The SocketResponseEvent object associated with this call.
		 */
		protected function joinedGameHandler(evt:SocketResponseEvent):void
		{
			_player = new Player(AssetManager.YHETI, myEvent.parameters.player_id);
			_player.setPlayerPosition(_map.playerSpawn, _map.playerSpawnDir);
			addPlayer(_player);
		}
		
		/*
		 *	Function that is excecuted once another player has joined the game, 
		 * 	aka the server has assigned a playerid to another client and we need
		 * 	to locally add a Player object to represent that player and add it
		 * 	to the list of Players that will be rendered.
		 * 
		 * 	@param	evt The SocketResponseEvent object associated with this call.
		 */
		protected function playerJoinedHandler(evt:SocketResponseEvent):void
		{
			var newPlayer:Player = new Player(AssetManager.YHETI, myEvent.parameters.player_id);
			
			if (myEvent.parameters.properties.x != undefined)
				newPlayer.setPlayerPosition(new Point(myEvent.parameters.properties.x,myEvent.parameters.properties.y), 1);
			else
				newPlayer.setPlayerPosition(_map.playerSpawn, _map.playerSpawnDir);
						
			addPlayer(newPlayer);
		}
		
		/*
		 *	Function that is excecuted once another player has left the game,
		 * 	we need to take that player out of the list of players in our Game
		 *  world since we no longer need to update/render that client.
		 * 
		 * 	@param	evt The SocketResponseEvent object associated with this call.
		 */
		protected function playerDisconnectedHandler(evt:SocketResponseEvent):void
		{
			removePlayer(evt.parameters.player_id);
		}
		
		/*
		 *	Function that is excecuted once another player has sent updates to
		 * 	the server and we need to reflect those updates in our client side
		 * 	simulation.
		 * 
		 * 	@param	evt The SocketResponseEvent object associated with this call.
		 */
		protected function playerStateChangedHandler(evt:SocketResponseEvent):void
		{
			var myPlayer:Player = getPlayer(myEvent.parameters.player_id);
			
			for(var i:String in myEvent.parameters.properties)
			{
				myPlayer[i] = myEvent.parameters.properties[i];
			}
		}
		
		/*
		 * 	Simply adds a player object to the list of players in the game so we can
		 * 	perform update calls etc...
		 */
		protected function addPlayer(player:Player):void
		{
			_players.push(player);
		}
		
		/*
		 *	Function that returns the player with the associated ID string or
		 * 	if that player does not exist the function will throw an Error.
		 * 
		 * 	@param id The ID String for the requested Player object
		 */
		protected function getPlayer(id:String):Player
		{
			var length:int = _players.length;
			var player:Player = null;
			for (var i:int = 0; i < length; i++)
			{
				if (_players[i].id == id)
					player = _players[i];
			}
			if (player == null)
				throw new Error( "Player does not exist in the System!" );
			return player;
		}

		/*
		 *	Function that will remove the player object with the associated ID
		 * 	string from the system. If the player object is not found, we throw 
		 * 	a new Error.
		 * 
		 * 	@param id The ID String for the requested Player object
		 */
		protected function removePlayer(id:String):void
		{
			var length:int = _players.length;
			var index:int = -1;
			for (var i:int = 0; i < length; i++)
			{
				if (_players[i].id == id)
					index = i;
			}
			if (index == -1)
				throw new Error( "Player does not exist in the System!" );
			else
				_players.splice(index, 1);
		}
	}
	
}